package com.atguigu.gulimall.search.thread;

import java.util.concurrent.*;

/**
 * 1）、继承 Thread
 * 2）、实现 Runnable 接口
 * 3）、实现 Callable 接口 + FutureTask （可以拿到返回结果，可以处理异常）
 * 4）、线程池
 *
 * 区别：1、2）不能得到返回值
 *      3）可以获得返回值
 *      但1、2、3都不能控制资源
 *      只有4）可以控制资源，好处是性能稳定
 */
public class ThreadTest {

    //当前系统中池的数量只有一两个，每个异步任务，提交给线程池让他自己去执行
    public static ExecutorService executor = Executors.newFixedThreadPool(10);

    public  void testThread1(String[] args) throws Exception{
        System.out.println("main start.....");
        //1）继承 Thread
//        Thread1 thread1 = new Thread1();
//        thread1.start();
        //2）实现 Runnable 接口
//        Runnable1 runnable1 = new Runnable1();
//        new Thread(runnable1).start();
        //3）实现 Callable 接口 + FutureTask （可以拿到返回结果，可以处理异常）
//        Callable01 callable01 = new Callable01();
//        FutureTask<String> stringFutureTask = new FutureTask<>(callable01);
//        new Thread(stringFutureTask).start();
        //阻塞等待整个线程执行完成，获得返回结果
//        System.out.println("main stop...."+stringFutureTask.get());



        //4）线程池：给线程池直接提交任务
        //我们以后在业务代码里面，以上三种启动线程的方式都不用，应该将所有的多线程异步任务交给线程池来执行

        /*
            创建线程池的几种方式：
            1.Executors.newFixedThreadPool(10);
            2.ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
                我们需要对里面的七大参数进行详解：
                int corePoolSize,  核心线程数(只要线程池不销毁便一直存在，除非我们设置了允许核心线程数超时删除的属性：allowCoreThreadTimeOut)。线程池在创建好后就准备就绪的线程数量，就等带来接受异步任务去执行
                int maximumPoolSize,   最大线程数量，主要用于控制资源，指的是当前可以运行的最大线程数
                long keepAliveTime, 存活时间。如果当前的线程数量大于核心线程数，只要线程空闲时间大于指定的存活时间，便会释放空闲线程，到核心线程数为止
                TimeUnit unit,  存活时间的单位
                BlockingQueue<Runnable> workQueue,  阻塞队列。如果任务有很多，就会将目前多的任务放到队列里面，只要有空闲线程，就会去队列里面取出新的任务继续执行
                ThreadFactory threadFactory,    线程的创建工厂。一般都是默认的，如果我们想在创建线程的时候带上名字，就可以自定义线程工厂
                RejectedExecutionHandler handler    拒绝执行处理器。如果任务的请求数量超出了“最大线程数量”，可以按照我们指定的拒绝策略拒绝执行任务

            在搞明白了线程池的七大核心参数后，我们需要弄明白这七个参数的顺序
            1）线程池创建，准备好 core 数量的核心线程，准备接受任务
            2）新的任务进来，用 core 准备好的空闲线程执行。
                2.1）corePoolSize 满了，就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队 列获取任务执行
                2.2）阻塞队列满了，就直接开新线程执行，最大只能开到 maximumPoolSize 指定的数量
                2.3）如果线程数开到了 max 的数量，还有新任务进来，就会使用 reject 指定的拒绝策 略进行处理
                2.4）max 都执行好了。（maximumPoolSize-corePoolSize）个数量空闲的线程会在 keepAliveTime 指定的时间后自 动销毁。最终保持到 corePoolSize 大小
            3）所有的线程创建都是由指定的 factory 创建的


            注意：关于阻塞队列(BlockingQueue)有很多种实现,我们采用的LinkedBlockingDeque默认最大队列长度
            为Integer的最大值，这是不好的，根据线程池的执行流程可知，不断往阻塞队列中添加任务最终会导致占满内存
            因此我们需要自己指定队列的长度，这一般是由我们通过压力测试来预估访问量大致数量来决定的

            面试题：
            一个线程池 core 7； max 20 ，queue：50，100 并发进来怎么分配的；
            先来的7个可以立即执行，然后50个放入阻塞队列中，阻塞队列满了，只能最多再开辟13个线程，剩余的30个任务将执行拒绝策略
         */

        new ThreadPoolExecutor( 10,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        /*
        常见的 4 种线程池
         newCachedThreadPool
             创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若 无可回收，则新建线程。
         newFixedThreadPool
             创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。
         newScheduledThreadPool
             创建一个定长线程池，支持定时及周期性任务执行。
         newSingleThreadExecutor
             创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务 按照指定顺序(FIFO, LIFO, 优先级)执行。
         */

        /*
        开发中为什么使用线程池
         降低资源的消耗
             通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
         提高响应速度
             因为线程池中的线程数没有超过线程池的最大上限时，有的线程处于等待分配任务的状态，当任务来时无需创建新的线程就能执行
         提高线程的可管理性
             线程池会根据当前系统特点对池内的线程进行优化处理，减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源，还降低系统的稳定性，使用线程池进行统一分配
         */
        System.out.println("main stop....");
    }


    public static class Thread1 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程为："+Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务执行结束！");
        }
    }

    public static class Runnable1 implements Runnable{
        @Override
        public void run() {
            System.out.println("当前线程为："+Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务执行结束！");
        }
    }

    public static class Callable01 implements Callable<String>{
        @Override
        public String call() throws Exception {
            System.out.println("当前线程为："+Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务执行结束！");
            return "大家好";
        }
    }

    public static void main(String[] args) throws Exception{
        System.out.println("main start.....");
//        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//            System.out.println("当前线程为：" + Thread.currentThread().getId());
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println("任务执行结束！");
//        }, executor);

        /*
        方法出现异常时可以通过下面的方式进行处理
         */
//        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程为：" + Thread.currentThread().getId());
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            int a = 1/0;
//            System.out.println("任务执行结束！");
//            return "大家好";
//        }, executor).whenComplete((result,exception)->{
//            //虽然能得到异常信息，但是没法修改返回数据
//            System.out.println("异步调用执行成功，结果是："+result+",异常是"+exception);
//        }).exceptionally(throwable -> {
//            //可以感知异常，同时返回默认值
//            return "我又好了";
//        });


        /*
        不论结果是否出现异常，我们都能对返回值作出一定的调整
         */
//        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程为：" + Thread.currentThread().getId());
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            int a = 1 / 0;
//            System.out.println("任务执行结束！");
//            return "大家好";
//        }, executor).handle((result, exception) -> {
//            if (result != null) {
//                return result + "哈哈哈";
//            }
//            if (exception != null) {
//                return "我又回来啦，哦哦哦哦哦哦";
//            }
//            return "完了，我啥都没有了";
//        });



        /*
        线程的串行化
         */
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程为：" + Thread.currentThread().getId());
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
////            int a = 1 / 0;
//            return "大家好";
//        }, executor).thenApplyAsync((result) -> {
//            System.out.println("任务2启动了。。。。。" + result);
//            return 1;
//        }, executor);


        /*
        两任务的组合执行，合并两个异步任务
     */
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程启动：" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务1线程结束");
            return "大家好";
        }, executor);


        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程启动：" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2线程结束");
            return 111111111;
        }, executor);

        CompletableFuture<String> future = future1.thenCombineAsync(future2, (f1, f2) -> {
            return "future1=" + f1 + ", future2=" + f2 + "hahahahh";
        }, executor);
        System.out.println("main stop...."+future.get());
    }


}
